//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#ifndef __ELASTOS_MEMSPY_H__
#define __ELASTOS_MEMSPY_H__

#include <car.h>
#include <assert.h>
#include <linknode.h>
#include <stdio.h>
#include <elapi.h>
#include <eladef.h>

_ELASTOS_NAMESPACE_USING

#if defined(MEMORYSPY)

//#define MSPY_DUMPCALLSTACK

#define HASH_SIZE_OF_PROCESS 1024
#define HASH_SIZE_OF_THREAD 8

#define MEMSPY_STACK_INVALID_DEGREE 3
#define MEMSPY_STACK_RECORD_DEPTH 8

#define MEMSPY_MAGIC 0x4D535059

#define MEMSPYFOROVERRUN 4
#define MEMSPYOVERRUNCHECKSUM 0x1BADBEEF

#ifndef HASHINDEXOFPROCESS
#define HASHINDEXOFPROCESS(TYPE, Address) \
        (TYPE)(((TYPE)Address >> 4) % HASH_SIZE_OF_PROCESS)
#endif

#ifndef HASHINDEXOFTHREAD
#define HASHINDEXOFTHREAD(TYPE, THREADID) \
        (TYPE)(((TYPE)THREADID) % HASH_SIZE_OF_THREAD)
#endif

#ifndef ISOVERRUN
    #ifdef _x86
        #define ISOVERRUN(PV, SIZE) \
            *(int *)((byte_t *)PV + SIZE) != MEMSPYOVERRUNCHECKSUM
    #else
        #define ISOVERRUN(PV, SIZE)  IsOverrun((byte_t *)PV, (UInt32)SIZE)
        INLINE _ELASTOS Boolean IsOverrun(byte_t * pDes ,UInt32 uOffset)
        {
            UInt32 uMSpyCheckSum = NULL;
            memcpy(&uMSpyCheckSum, pDes + uOffset, sizeof(UInt32));

            return MEMSPYOVERRUNCHECKSUM != uMSpyCheckSum;
        }
    #endif // _x86
#endif

#ifndef IS_INVALID_HEAPBLOCK
#define IS_INVALID_HEAPBLOCK(pv) \
    (((PMEMSPYNODE)((byte_t *)pv - c_uMemSpyNodeSize))->uMagic != MEMSPY_MAGIC)
#endif

#ifndef MEMSPYREPORT
#define BEGIN_MEMLEAK_DETECT_REPORT() \
        printf("\n***********Memory Leak Detected Report***********\n")

#define END_MEMLEAK_DETECT_REPORT() \
        printf("\n-------------------------------------------------\n"); \
        printf("\n*************************************************\n")

#define REPORT_ONE_MEMLEAK(MSPYINDEX, MSPYADDRESS, MSPYSIZE) \
        printf("\n-------------------------------------------------\n"); \
        printf("\nindex = 0x%x    address = 0x%08x    size=%d\n", \
                MSPYINDEX, MSPYADDRESS, MSPYSIZE)

#define REPORT_TOTAL_MEMLEAKS(MSPYTOTALLEAKS) \
        do { \
            printf("Total memory leaks: %d\n", MSPYTOTALLEAKS); \
            printf("Use \"memtrace -v\" to show detailed report," \
                " \"memtrace -d\" to debug leaks.\n"); \
        } while (0)

#endif

typedef struct MEMORYSPYNODE : public DLinkNode {
    UInt32 uMagic;
    PVoid pvRequestAllocedBlock;
    UInt32 uRequestAllocedSize;
    UInt32 uAllocIndexInProcess;
    UInt32 nThreadID;
    UInt32 uAllocIndexInThread;
    Address addrCallStack[MEMSPY_STACK_RECORD_DEPTH];
    bool   isAllocedbyVirtual;
    UInt32 pad; // pad for align 8 byte
} MEMSPYNODE, *PMEMSPYNODE;

const size_t c_uMemSpyNodeSize = sizeof(MEMSPYNODE);

typedef struct MEMORYSPYTHREAD : public DLinkNode {
    UInt32 uThreadID;
    UInt32 uCurrentAllocedTimesOfThread;
} MEMSPYTHREAD, *PMEMSPYTHREAD;

// The following MSpy functions to be used in the memory spy
_ELASTOS Boolean MemSpyInitMemSpyArch();
_ELASTOS Boolean MemSpyExitAndReportMemSpyInfo();

UInt32 MSpyExpandRequestSize(const UInt32 ulRequestAllocSize);
PVoid MSpyRetrieveRequestHeapBlock(const PVoid pMSpyAllocHeapBlock);
Void MSpyGetThreadInfoOfMemSpyNode(PMEMSPYNODE pMemSpyNode);
Void MSpyAddMemSpyNodeInfoToProcessHash(const PVoid pRequestedHeapBlock,
                                        const UInt32 uRequestAllocedSize);
PMEMSPYTHREAD MSpyNewMemSpyThreadNode(const UInt32 nID);
Void MSpyDumpCallStack(Address *pMemSpyNodeStack);
PVoid MSpyRetrieveExpandedHeapBlock(const PVoid pRequestHeapBlock);
PMEMSPYNODE MSpySearchMemSpyNodeFromProcessHash(
                                      const PVoid pRequestHeapBlock);
Void MSpyDetachMemSpyNodeFromProcessHash(const PMEMSPYNODE pMemSpyNode);
_ELASTOS Boolean MSpyHasMemoryLeak();
Void MSpyEmptyThreadHashList();
Void MSpyOutputCallStack(const PMEMSPYNODE pMemSpyNode);

Void MSpySetArgs(wchar_t uArgId, UInt32 uStopIndex, Address addr);
ECode AddToProcessHash(PVoid  pVAddress, UInt32 size);
Void RemoveFromProcessHash(PVoid pVAddress);

// The following MemSpy functions to be used to replace '_CMemoryHeap_Alloc',
// '_CMemoryHeap_Free', '_CMemoryHeap_Realloc' and so on defined in taskmem.cpp.

ELAPI_(PVoid) _CMemoryHeap_Alloc(MemorySize size, Boolean zeroMem);
ELAPI_(void) _CMemoryHeap_Free(PVoid pMem);
ELAPI_(PVoid) _CMemoryHeap_Realloc(PVoid pMem, MemorySize size);
#endif //#if defined MEMORYSPY

#endif //__ELASTOS_MEMSPY_H__
